/*
Author: Surpass-HC
Source: https://gitee.com/hxchuan000/clpc-1
Mail: surpass-hc@qq.com
*/
#include <cirqueue.h>
// check total len, if que is full, realloc * 2.
static int cCirQueChkReTotal(cCirQueRoot *cqptr)
{
    void **tmpptr;
    long tmpTotal;
    if (cqptr->len < cqptr->total)
    {
        return 0;
    }
    else if (cqptr->len > cqptr->total)
    {
        logdebugc("len > total:%ld, %ld", cqptr->len, cqptr->total);
        return -2;
    }
    tmpTotal = cqptr->total * 2;
    if (tmpTotal == 0)
    {
        tmpTotal = CDEQUE_INIT_SIZE;
    }
    if (tmpTotal >= CDEQUE_WARN_SIZE)
    {
        logdebugc("size warn:%ld", tmpTotal);
    }
    logdebugc("need realloc:l=%ld, t=%ld, n=%ld", cqptr->len, cqptr->total, tmpTotal);
    tmpptr = (void **)realloc(cqptr->ptr, sizeof(void *) * tmpTotal);
    if (!tmpptr)
    {
        logdebugc("alloc fail:%ld", tmpTotal);
        return -1;
    }
    cqptr->ptr = tmpptr;
    cqptr->total = tmpTotal;
    if (cqptr->len == 0)
    {
        return 1;
    }
    if (cqptr->wid > cqptr->rid)
    {
        return 1;
    }
    memcpy(cqptr->ptr + (cqptr->total - (cqptr->len - cqptr->rid)), cqptr->ptr + cqptr->rid, (cqptr->len - cqptr->rid) * sizeof(void *));
    cqptr->rid = cqptr->total - (cqptr->len - cqptr->rid);
    // logdebugc("r=%ld,w=%ld,len=%ld,t=%ld", cqptr->rid, cqptr->wid, cqptr->len, cqptr->total);
    // for(long i = 0; i < cqptr->len; i++)
    // {
    //     long tmpid = (i + cqptr->rid) % cqptr->total;
    //     logdebugc("val[%ld]%p", tmpid, cqptr->ptr[tmpid]);
    // }
    return 1;
}
static void cDequeLock(cCirQueRoot *cqptr)
{
    if (cqptr->withmt)
    {
        pthread_mutex_lock(&cqptr->mut);
    }
}
static void cDequeUnlock(cCirQueRoot *cqptr)
{
    if (cqptr->withmt)
    {
        pthread_mutex_unlock(&cqptr->mut);
    }
}

int cCirQueInit(cCirQueRoot *cqptr, __compar_fn_t cfgcmp, int cfgWl)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    memset(cqptr, 0, sizeof(cCirQueRoot));
    cqptr->cmpfn = cfgcmp;
    cqptr->withmt = cfgWl;
    pthread_mutex_init(&cqptr->mut, NULL);
    return 0;
}
int cCirQueFree(cCirQueRoot *cqptr)
{
    int tmpflag = 1;
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    cDequeLock(cqptr);
    do
    {
        if (cqptr->len > 0)
        {
            logerrorc("que have data, not free! pull data first!");
            tmpflag = -1;
            break;
        }
        if (cqptr->ptr)
        {
            free(cqptr->ptr);
            cqptr->ptr = NULL;
        }
        cqptr->len = 0;
        cqptr->rid = 0;
        cqptr->wid = 0;
        cqptr->total = 0;
    } while (0);
    cDequeUnlock(cqptr);
    if (tmpflag >= 0)
    {
        pthread_mutex_destroy(&cqptr->mut);
        memset(cqptr, 0, sizeof(cCirQueRoot));
    }
    return tmpflag;
}
long cCirQueSize(cCirQueRoot *cqptr)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    return cqptr->len;
}
int cCirQuePushBack(cCirQueRoot *cqptr, void *ptr)
{
    int tmpflag = 1;
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    cDequeLock(cqptr);
    do
    {
        if (cCirQueChkReTotal(cqptr) < 0)
        {
            logerrorc("");
            tmpflag = -2;
            break;
        }
        cqptr->ptr[cqptr->wid] = ptr;
        cqptr->wid = CYCLE_ADD(cqptr->wid, 1, cqptr->total);
        cqptr->len++;
    } while (0);
    cDequeUnlock(cqptr);
    return tmpflag;
}
int cCirQuePushFront(cCirQueRoot *cqptr, void *ptr)
{
    int tmpflag = 1;
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    cDequeLock(cqptr);
    do
    {
        if (cCirQueChkReTotal(cqptr) < 0)
        {
            logerrorc("");
            tmpflag = -2;
            break;
        }
        cqptr->rid = CYCLE_ADD(cqptr->rid, -1, cqptr->total);
        cqptr->ptr[cqptr->rid] = ptr;
        cqptr->len++;
    } while (0);
    cDequeUnlock(cqptr);
    return tmpflag;
}
int cCirQuePullFront(cCirQueRoot *cqptr, void **optr)
{
    int tmpflg = 0;
    void *tmpval = NULL;
    if (!(cqptr && optr))
    {
        logerrorc("");
        return tmpflg;
    }
    cDequeLock(cqptr);
    do
    {
        if (cqptr->len < 1ul)
        {
            break;
        }
        tmpval = cqptr->ptr[cqptr->rid];
        cqptr->rid = CYCLE_ADD(cqptr->rid, 1, cqptr->total);
        cqptr->len--;
        tmpflg = 1;
    } while (0);
    cDequeUnlock(cqptr);
    *optr = tmpval;
    return tmpflg;
}
int cCirQuePullBack(cCirQueRoot *cqptr, void **optr)
{
    int tmpflg = 0;
    void *tmpval = NULL;
    if (!(cqptr && optr))
    {
        logerrorc("");
        return tmpflg;
    }
    cDequeLock(cqptr);
    do
    {
        if (cqptr->len < 1ul)
        {
            break;
        }
        cqptr->wid = CYCLE_ADD(cqptr->wid, -1, cqptr->total);
        tmpval = cqptr->ptr[cqptr->wid];
        cqptr->len--;
        tmpflg = 1;
    } while (0);
    cDequeUnlock(cqptr);
    *optr = tmpval;
    return tmpflg;
}
static int cCirQueZeroBegin(cCirQueRoot *cqptr)
{
    int tmpflag = 1;
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    cDequeLock(cqptr);
    do
    {
        if (cqptr->rid == 0)
        {
            break;
        }
        else if (cqptr->rid < cqptr->wid)
        {
            for (int i = 0; i < cqptr->wid - cqptr->rid; i++)
            {
                cqptr->ptr[i] = cqptr->ptr[cqptr->rid + i];
            }
            cqptr->rid = 0;
            cqptr->wid = cqptr->wid - cqptr->rid;
        }
        else
        {
            void **tmpptr = (void **)malloc(sizeof(void *) * cqptr->total);
            if (!tmpptr)
            {
                logdebugc("");
                tmpflag = -1;
                break;
            }
            for (int i = 0; i < cqptr->total - cqptr->rid; i++)
            {
                tmpptr[i] = cqptr->ptr[cqptr->rid + i];
            }
            for (int i = 0; i < cqptr->wid; i++)
            {
                tmpptr[cqptr->total - cqptr->rid + i] = cqptr->ptr[i];
            }
            free(cqptr->ptr);
            cqptr->ptr = tmpptr;
            cqptr->rid = 0;
            cqptr->wid = (cqptr->rid + cqptr->len) % cqptr->total;
        }
    } while (0);
    cDequeUnlock(cqptr);
    return tmpflag;
}
int cCirQueSort(cCirQueRoot *cqptr)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    if (!cqptr->cmpfn)
    {
        logdebugc("no cmp fn");
        return -1;
    }
    cCirQueZeroBegin(cqptr);
    cDequeLock(cqptr);
    qsort(cqptr->ptr, cqptr->len, sizeof(cqptr->ptr[0]), cqptr->cmpfn);
    cDequeUnlock(cqptr);
    return 1;
}
long cCirQueSearch(cCirQueRoot *cqptr, void *findKey)
{
    if (!cqptr)
    {
        logerrorc("");
        return -1;
    }
    if (!cqptr->cmpfn)
    {
        logdebugc("no cmp fn");
        return -1;
    }
    void **tmpptr = bsearch(&findKey, cqptr->ptr, cqptr->len, sizeof(cqptr->ptr[0]), cqptr->cmpfn);
    logdebugc("%p,%p,%ld", tmpptr, cqptr->ptr, cqptr->len);
    if (!tmpptr)
    {
        return -1;
    }
    else
    {
        long tmpid = ((long)tmpptr - (long)cqptr->ptr) / (long)sizeof(void *);
        return tmpid;
    }
}
